<template>
    <el-aside width="300px">
        <template v-if="project">
            <AsideHeader ref="header" v-model="appName" :project="project" :save="save">
                <tnxel-button link icon="Refresh" tooltip="刷新工程" @click="loadProject"/>
            </AsideHeader>
            <div class="class-tree-container">
                <el-scrollbar always>
                    <el-tree ref="classTree"
                        :data="treeDataList"
                        node-key="key"
                        :props="treeProps"
                        :show-checkbox="selectable"
                        :default-checked-keys="checkedKeys"
                        :key="selectable"
                        empty-text="加载中..."
                        :default-expanded-keys="expandedKeys"
                        highlight-current
                        :expand-on-click-node="false"
                        draggable
                        :allow-drag="allowNodeDrag"
                        :allow-drop="allowNodeDrop"
                        @node-drop="onNodeDrop"
                        @node-expand="onNodeExpand"
                        @node-collapse="onNodeCollapse"
                        @check="selectNode"
                    >
                        <template #default="{data, node}">
                            <div class="flex-v-center flex-1">
                                <NodeIcon :type="data.type.toString()" @dblclick="openNode(data)"/>
                                <div class="node-name overflow-ellipsis"
                                    :class="{'flex-v-center': data.type === 'libs'}"
                                    :style="{'max-width': nodeMaxWidth(node)}"
                                    :title="(data.type !== 'app' && data.simpleName) ? data.name : undefined"
                                    @dblclick="openNode(data)"
                                >
                                    <span :class="{'abstract-class-name': data.abstractClass}"
                                        v-if="data.simpleName">{{ data.simpleName }}</span>
                                    <span class="text-placeholder" v-else>&lt;default&gt;</span>
                                    <span class="text-placeholder ms-1" v-if="data.caption">({{ data.caption }})</span>
                                    <tnxel-icon value="Loading" class="ms-1 text-placeholder" style="margin-top: 2px;"
                                        v-if="data.type === 'libs' && data.loading"/>
                                </div>
                                <div class="node-action">
                                    <template v-if="data.type === 'models'">
                                        <tnxel-icon class="link me-2"
                                            value="bi bi-chevron-expand"
                                            title="全部展开"
                                            @click="expandAll(node,true)"
                                        />
                                        <tnxel-icon class="link me-3"
                                            value="bi bi-chevron-contract"
                                            title="全部收起"
                                            @click="expandAll(node,false)"
                                        />
                                    </template>
                                    <template v-else-if="data.type === 'libs'">
                                        <tnxel-icon class="link me-3" value="SetUp" title="建模依赖库管理"
                                            @click="toModelingDependencies(data.appName)"/>
                                    </template>
                                    <template v-else-if="data.type === 'database'">
                                    </template>
                                    <template v-else-if="data.type === 'package'">
                                        <el-dropdown v-if="data.simpleName">
                                            <tnxel-icon class="link me-3" value="bi bi-three-dots-vertical"/>
                                            <template #dropdown>
                                                <el-dropdown-menu>
                                                    <el-dropdown-item>
                                                        <tnxel-button type="primary" link icon="Edit"
                                                            @click="toUpdatePackage(data.appName, data.name)">修改包名
                                                        </tnxel-button>
                                                    </el-dropdown-item>
                                                    <el-dropdown-item>
                                                        <tnxel-button type="primary" link icon="Plus"
                                                            @click="toAddClass(data)">添加模型类
                                                        </tnxel-button>
                                                    </el-dropdown-item>
                                                </el-dropdown-menu>
                                            </template>
                                        </el-dropdown>
                                        <tnxel-icon class="link me-3" value="Plus" title="在该包中添加模型类"
                                            @click="toAddClass(data)" v-else/>
                                    </template>
                                    <template v-else-if="data.type === 'lib' || data.type === 'lib-fixed'">
                                        <tnxel-icon class="text-placeholder me-3"
                                            value="Loading"
                                            v-if="data.loading"/>
                                        <tnxel-icon class="link me-3"
                                            value="Refresh"
                                            title="重新加载"
                                            @click="reloadLib(data)"
                                            v-else-if="data.groupId === 'java'"/>
                                    </template>
                                    <el-dropdown v-else>
                                        <tnxel-icon class="link me-3" value="bi bi-three-dots-vertical"/>
                                        <template #dropdown>
                                            <el-dropdown-menu>
                                                <el-dropdown-item>
                                                    <tnxel-button type="primary" link icon="Edit"
                                                        @click="toUpdateClass(data)">修改
                                                    </tnxel-button>
                                                </el-dropdown-item>
                                                <el-dropdown-item>
                                                    <tnxel-button type="primary" link icon="Delete"
                                                        @click="toDeleteClass(data)">删除
                                                    </tnxel-button>
                                                </el-dropdown-item>
                                                <el-dropdown-item>
                                                    <tnxel-button type="primary" link icon="CopyDocument"
                                                        @click="toCopyClass(data)">复制
                                                    </tnxel-button>
                                                </el-dropdown-item>
                                                <el-dropdown-item divided>
                                                    <tnxel-button type="primary" link icon="fa-solid fa-sitemap"
                                                        @click="toShowInheritanceHierarchy(data)">继承层次
                                                    </tnxel-button>
                                                </el-dropdown-item>
                                            </el-dropdown-menu>
                                        </template>
                                    </el-dropdown>
                                </div>
                            </div>
                        </template>
                    </el-tree>
                </el-scrollbar>
            </div>
            <div class="footer">
                <tnxel-button type="primary" plain icon="Plus" @click="toAddClass">添加模型类</tnxel-button>
            </div>
        </template>
        <div class="h-100 w-100 flex-center" v-else>
            <tnxel-icon value="Loading" class="text-muted" :size="24"/>
        </div>
    </el-aside>
</template>

<script>
import tnx from '@/tnx';
import ModelingProject from '@/logic/modeling/entity/ModelingProject.js';
import ModelingApp from '@/logic/modeling/entity/ModelingApp.js';
import TypeKind from '@/logic/modeling/enums/TypeKind.js';
import PersistUtil from '@/logic/modeling/util/persist.js';
import jdkLib from '@/logic/modeling/util/dependency-lib-jdk.js';
import truenewxLib from '@/logic/modeling/util/dependency-lib-truenewx.js';
import MavenProjectUtil from '@/logic/maven/util/project.js';

import AsideHeader from './aside-header.vue';
import packageUpdate from './package-update.vue';
import classAdd from './class-add.vue';
import classUpdate from './class-update.vue';
import inheritanceHierarchy from './inheritance-hierarchy.vue';
import modelingDependencies from './modeling-dependencies.vue';
import NodeIcon from './components/NodeIcon.vue';

const getPackageNodeKey = function (appName, packageName) {
    return 'package-' + appName + '-' + packageName;
}

const getClassNodeKey = function (appName, clazz) {
    return 'class-' + appName + '-' + clazz.id;
}

export default {
    components: {AsideHeader, NodeIcon},
    props: {
        pomPath: {
            type: String,
            required: true,
        },
        selectable: Boolean, // 是否选取模式
    },
    emits: ['refresh', 'to-edit', 'class-deleted', 'package-change', 'class-change', 'selected'],
    data() {
        return {
            project: null,
            appName: null,
            checkedKeys: [],
            expandedKeys: [],
        };
    },
    computed: {
        currentApp() {
            if (this.project && this.appName) {
                return this.project.apps.find(app => app.name === this.appName);
            }
            return null;
        },
        treeProps() {
            if (this.selectable) {
                return {
                    disabled: data => {
                        if (this.currentApp?.dependencyInitializing) {
                            return true;
                        }
                        return data.type === 'libs' || data.type === 'lib' || data.type === 'lib-fixed'
                            || data.type === 'database';
                    }
                }
            }
            return undefined;
        },
        treeDataList() {
            let treeDataList = [];
            if (this.currentApp) {
                let app = this.currentApp;
                let appNodeDataList = [];
                let classNodeDataList = [];
                appNodeDataList.push({
                    key: 'models-' + app.name,
                    type: 'models',
                    appName: app.name,
                    simpleName: '模型',
                    caption: app.classes.length,
                    children: classNodeDataList,
                });

                for (let clazz of app.classes) {
                    let packageName = clazz.packageName;
                    let packageNodeKey = getPackageNodeKey(app.name, packageName);
                    let packageNodeData = classNodeDataList.find(data => data.key === packageNodeKey);
                    if (!packageNodeData) {
                        packageNodeData = {
                            key: packageNodeKey,
                            type: 'package',
                            appName: app.name,
                            name: packageName,
                            simpleName: packageName,
                            children: [],
                        }
                        classNodeDataList.push(packageNodeData);
                        classNodeDataList.sort((data1, data2) => data1.name.localeCompare(data2.name));
                    }
                    let classNodeData = Object.assign({}, clazz, {
                        key: getClassNodeKey(app.name, clazz),
                        type: clazz.kind.name,
                        appName: app.name,
                        name: clazz.getName(),
                    });
                    packageNodeData.children.push(classNodeData);
                }

                // 按基础包重新组织节点
                let basePackageNodeData = null;
                let basePackageName = app.getBasePackageName();
                if (basePackageName) {
                    basePackageNodeData = {
                        key: 'package-' + app.name + '-' + basePackageName,
                        type: 'package',
                        appName: app.name,
                        name: basePackageName,
                        simpleName: basePackageName,
                        children: [],
                    }
                    let emptyPackageNodeData = null;
                    for (let data of classNodeDataList) {
                        if (data.simpleName) {
                            data.simpleName = data.simpleName.substring(basePackageName.length + 1);
                            if (data.simpleName) {
                                basePackageNodeData.children.push(data);
                            } else {
                                basePackageNodeData.children.push(...data.children);
                            }
                        } else {
                            emptyPackageNodeData = data;
                        }
                    }
                    // 清空数组，重新装填
                    classNodeDataList.length = 0;
                    classNodeDataList.push(basePackageNodeData);
                    if (emptyPackageNodeData) {
                        classNodeDataList.unshift(emptyPackageNodeData);
                    }
                }

                let dependencyNodeDataList = [];
                if (app.dependencies) {
                    for (let dependency of app.dependencies) {
                        dependencyNodeDataList.push(this.dependencyLibToTreeNodeData(dependency));
                    }
                }

                appNodeDataList.push({
                    key: 'libs-' + app.name,
                    type: 'libs',
                    appName: app.name,
                    simpleName: '依赖库',
                    children: dependencyNodeDataList,
                    loading: app.dependencyInitializing,
                });

                appNodeDataList.push({
                    key: 'database-' + app.name,
                    type: 'database',
                    simpleName: '数据库：' + (app.databaseVendor || '未知'),
                });

                treeDataList = appNodeDataList;
            }

            return treeDataList;
        },
    },
    watch: {
        appName() {
            this.$nextTick(() => {
                setTimeout(() => {
                    this.expandDefaultTreeNode();
                    this.checkDependencyLibsSync();
                });
            });
        },
    },
    mounted() {
        this.loadProject();
    },
    methods: {
        nodeMaxWidth(node) {
            let maxWidth = 221 - 18 * (node.level - 1);
            if (maxWidth > 0) {
                return maxWidth + 'px';
            }
            return undefined;
        },
        loadProject() {
            tnx.showLoading();
            MavenProjectUtil.loadProject(this.pomPath, true).then(mavenProject => {
                this.project = new ModelingProject(mavenProject);
                if (mavenProject.modules.length) {
                    if (this.project.isSingleArch()) { // 单体架构，从maven根工程目录加载建模应用
                        this.loadApp(mavenProject, app => {
                            this.project.apps.push(app);
                            this.onLoaded();
                        });
                    } else { // 微服务架构，从maven子模块目录加载建模应用
                        const loadPromises = mavenProject.modules.map(module => {
                            return new Promise(resolve => {
                                this.loadApp(module, app => {
                                    resolve(app);
                                });
                            });
                        });

                        Promise.all(loadPromises).then(apps => {
                            this.project.apps = apps;
                            this.onLoaded();
                        });
                    }
                } else {
                    tnx.error('没有子模块，不能进行建模', () => {
                        if (this.$refs.header) {
                            this.$refs.header.toMavenProject();
                        }
                    });
                }
            });
        },
        loadApp(mavenProject, callback) {
            let appPath = typeof mavenProject === 'string' ? mavenProject : PersistUtil.getAppPath(mavenProject);
            tnx.util.file.exists(appPath).then(exists => {
                if (exists) {
                    PersistUtil.loadApp(appPath).then(app => {
                        this.prepareApp(mavenProject, app, callback);
                    }).catch(error => {
                        tnx.error(error);
                    });
                } else {
                    // 建模应用文件不存在，则创建一个默认的应用对象
                    let app = new ModelingApp(mavenProject.artifactId);
                    app.path = appPath;
                    PersistUtil.loadModelDependencyLibs(app).then(() => {
                        this.prepareApp(mavenProject, app, callback);
                    }).catch(error => {
                        tnx.error(error);
                    });
                }
            });
        },
        prepareApp(mavenProject, app, callback) {
            if (!app.databaseVendor) {
                MavenProjectUtil.readDatabaseVendor(mavenProject).then(databaseVendor => {
                    app.databaseVendor = databaseVendor;
                    callback(app);
                }).catch(errorMessage => {
                    tnx.error(errorMessage + '，不能进行建模', () => {
                        if (this.$refs.header) {
                            this.$refs.header.toMavenProject();
                        }
                    });
                });
            } else {
                callback(app);
            }
            app.dependencyInitializing = true;
            // 延时加载依赖库，以加快初始界面展现时间
            this.$nextTick(() => {
                setTimeout(() => {
                    let version = MavenProjectUtil.getVersion(mavenProject);
                    jdkLib.load(version.jdk, lib => {
                        app.dependencies.push(lib);

                        truenewxLib.loadModels(version.truenewx, libs => {
                            for (let lib of libs) {
                                app.dependencies.push(lib);
                            }
                            app.sortDependencies();
                            app.dependencyInitializing = false;
                            tnx.app.eventBus.emit('app-changed', app.name);
                            this.resetTreeData();
                        });
                        truenewxLib.loadConverters(version.truenewx);
                    });
                }, 100);
            });
        },
        onLoaded() {
            this.project.apps.sort((a1, a2) => a1.name.localeCompare(a2.name));

            let storageKey = this.$refs.header.getAppNameStorageKey();
            this.appName = localStorage.getItem(storageKey) || this.project.apps[0]?.name;
            tnx.closeLoading();
            this.$nextTick(() => {
                setTimeout(() => {
                    this.$emit('refresh', this.project);
                    this.expandDefaultTreeNode();
                    this.checkDependencyLibsSync();
                });
            });
        },
        expandDefaultTreeNode() {
            let firstNode = this.treeDataList[0];
            if (firstNode) {
                if (firstNode.children.length === 1) {
                    let child = firstNode.children[0];
                    // 第一级包目录下如果存在下级包目录，则展开第一级包目录
                    if (child.children?.length && child.children.some(node => node.type === 'package')) {
                        firstNode = child;
                    }
                }
                this.expandTreeNode(firstNode.key, true);
            }
        },
        expandTreeNode(nodeKey, expandParent = false) {
            if (nodeKey && this.$refs.classTree) {
                let node = this.$refs.classTree.getNode(nodeKey);
                const expandNode = n => {
                    if (n) {
                        if (n.childNodes.length) { // 有子节点的才可以展开
                            n.expanded = true;
                        }
                        this.onNodeExpand(n.data, n);
                        if (expandParent && n.parent) {
                            expandNode(n.parent);
                        }
                    }
                }
                expandNode(node);
            }
        },
        collapseTreeNode(nodeKey) {
            if (nodeKey && this.$refs.classTree) {
                let node = this.$refs.classTree.getNode(nodeKey);
                if (node) {
                    node.expanded = false;
                    this.onNodeCollapse(node.data, node);
                }
            }
        },
        expandAll(node, expanded) {
            node.expanded = expanded;
            if (node.childNodes) {
                for (let childNode of node.childNodes) {
                    this.expandAll(childNode, expanded);
                }
            }
        },
        onNodeExpand(data, node) {
            if (node.key && !this.expandedKeys.includes(node.key)) {
                this.expandedKeys.push(node.key);
            }
        },
        onNodeCollapse(data, node) {
            this.expandedKeys.remove(node.key);
            for (let childNode of node.childNodes) {
                this.onNodeCollapse(childNode.data, childNode);
            }
        },
        selectNode(data, checked) {
            let classKeys = [];
            let classIds = [];
            for (let data of checked.checkedNodes) {
                if (data.kind) {
                    classKeys.push(data.key);
                    classIds.push(data.id);
                }
            }
            this.checkedKeys = classKeys;
            this.$emit('selected', classIds);
        },
        // 判断节点可否被拖动
        allowNodeDrag(draggingNode) {
            let type = draggingNode.data.type;
            return type === 'package' || TypeKind.valueOf(type);
        },
        allowNodeDrop(draggingNode, dropNode, type) {
            if (type === 'inner') { // 只能拖到目标节点中，不能是前和后
                let sourceType = draggingNode.data.type;
                let sourceAppName = draggingNode.data.appName;
                let targetType = dropNode.data.type;
                let targetAppName = targetType === 'app' ? dropNode.data.name : dropNode.data.appName;
                if (sourceType === 'package') { // 移动包
                    // 包可以移动到其它应用的模型节点，但不能移动到自身的
                    if (targetType === 'models') {
                        return sourceAppName !== targetAppName;
                    }
                    // 顶级包只能移动至模型节点，不能移动至其它包
                    if (draggingNode.parent.data.type !== 'package') {
                        return false;
                    }
                    return targetType === 'package';
                } else { // 移动类
                    // 类可以移动到模型节点和包中
                    return targetType === 'models' || targetType === 'package';
                }
            }
            return false;
        },
        onNodeDrop(draggingNode, dropNode) {
            let sourceType = draggingNode.data.type;
            let sourceAppName = draggingNode.data.appName;
            let targetType = dropNode.data.type;
            let targetAppName = targetType === 'app' ? dropNode.data.name : dropNode.data.appName;
            if (sourceType === 'package') { // 移动包
                let newPackageName = '';
                if (targetType !== 'models') {
                    newPackageName = dropNode.data.name + '.';
                }
                newPackageName += draggingNode.data.simpleName;
                let sourceApp = this.project.findApp(sourceAppName);
                this.updatePackage(sourceApp, draggingNode.data.name, targetAppName, newPackageName);
            } else { // 移动类
                let newPackageName = '';
                if (targetType !== 'models') {
                    newPackageName = dropNode.data.name;
                }
                let sourceApp = this.project.findApp(sourceAppName);
                let sourceClass = sourceApp.findClass(draggingNode.data.name);
                let targetClass = Object.assign({}, sourceClass);
                targetClass.packageName = newPackageName;
                this.updateClass(sourceApp, sourceClass, targetAppName, targetClass);
            }
        },
        resetTreeData() {
            let apps = this.project.apps;
            this.project.apps = [];
            this.$nextTick(() => {
                this.project.apps = apps;
            });
        },
        checkDependencyLibsSync() {
            if (this.currentApp && this.currentApp.needsSyncDependencies()) {
                tnx.confirm('有建模依赖库已改动，去进行同步吗？', {
                    confirmButtonText: '去同步...',
                }).then(yes => {
                    if (yes) {
                        this.toModelingDependencies();
                    }
                });
            }
        },
        dependencyLibToTreeNodeData(lib) {
            // 建模和其它依赖库为普通依赖库，Java和Truenewx为固定依赖库
            let type = lib.type.ordinal > 1 ? 'lib' : 'lib-fixed';
            let name = lib.getName();
            if (name.startsWith('java:java:')) {
                name = name.substring('java:'.length);
            }
            return {
                key: type + '-' + name,
                type: type,
                name: name,
                simpleName: lib.artifactId + ':' + lib.version,
                groupId: lib.groupId,
                artifactId: lib.artifactId,
                version: lib.version,
            };
        },
        save(app, callback) {
            PersistUtil.saveApp(app, this.path).then(() => {
                if (typeof callback === 'function') {
                    callback();
                }
            });
        },
        openNode(data, expanded) {
            if (data) {
                if (data.type === 'app' || data.type === 'models' || data.type === 'libs' || data.type === 'package') {
                    let nodeKey = data.key;
                    if (expanded === undefined) {
                        if (this.expandedKeys.includes(nodeKey)) {
                            this.collapseTreeNode(nodeKey);
                        } else {
                            this.expandTreeNode(nodeKey);
                        }
                    } else if (expanded) {
                        this.expandTreeNode(nodeKey);
                    } else {
                        this.collapseTreeNode(nodeKey);
                    }
                } else {
                    this.toEditClassDetail(data.appName, data);
                }
            }
        },
        toUpdatePackage(appName, packageName) {
            let app = this.project.findApp(appName);
            tnx.open(packageUpdate, {
                appName: app.name,
                packageName: packageName,
                after: (newAppName, oldPackageName, newPackageName) => {
                    this.updatePackage(app, oldPackageName, newAppName, newPackageName, () => {
                        tnx.toast('修改成功');
                    });
                }
            });
        },
        updatePackage(oldApp, oldPackageName, newAppName, newPackageName, callback) {
            if (oldApp.name === newAppName) { // 同一个应用内修改
                if (oldPackageName !== newPackageName) {
                    let tempApp = oldApp.clone();
                    for (let clazz of tempApp.classes) {
                        if (clazz.packageName.startsWith(oldPackageName)) {
                            clazz.packageName = newPackageName + clazz.packageName.substring(oldPackageName.length);
                            let newClassName = clazz.getName();
                            if (tempApp.classes.some(c => c.getName() === newClassName && c.id !== clazz.id)) {
                                tnx.error('已存在 ' + newClassName + '，不可进行本次包名改动');
                                return;
                            }
                        }
                    }
                    tempApp.sortClasses();
                    this.save(tempApp, () => {
                        oldApp.classes = tempApp.classes;
                        tnx.app.eventBus.emit('app-changed', oldApp.name);
                        this.$emit('package-change', oldPackageName, newPackageName);
                        if (typeof callback === 'function') {
                            callback();
                        }
                        this.$nextTick(() => {
                            this.expandTreeNode(getPackageNodeKey(newAppName, newPackageName), true);
                        });
                    });
                }
            } else { // 跨应用修改
                let oldTempApp = oldApp.clone();
                let newApp = this.project.findApp(newAppName);
                let newTempApp = newApp.clone();
                for (let i = oldTempApp.classes.length - 1; i >= 0; i--) {
                    let clazz = oldTempApp.classes[i];
                    if (clazz.packageName.startsWith(oldPackageName)) {
                        clazz.packageName = newPackageName + clazz.packageName.substring(oldPackageName.length);
                        if (newTempApp.classes.some(c => c.getName() === clazz.getName() && c.id !== clazz.id)) {
                            tnx.error('应用 ' + newAppName + '中已存在 ' + clazz.getName() + '，不可进行本次包名改动');
                            return;
                        }
                        oldTempApp.classes.splice(i, 1);
                        newTempApp.classes.push(clazz);
                    }
                }
                newTempApp.sortClasses();
                this.save(oldTempApp, () => {
                    this.save(newTempApp, () => {
                        oldApp.classes = oldTempApp.classes;
                        newApp.classes = newTempApp.classes;
                        tnx.app.eventBus.emit('app-changed', oldApp.name);
                        tnx.app.eventBus.emit('app-changed', newApp.name);
                        this.$emit('package-change', oldPackageName, newPackageName);
                        if (typeof callback === 'function') {
                            callback();
                        }
                        this.$nextTick(() => {
                            this.expandTreeNode(getPackageNodeKey(newAppName, newPackageName), true);
                        });
                    });
                });
            }
        },
        toAddClass(data) {
            let appName = this.appName;
            let packageName = '';
            if (data) {
                appName = data.appName;
                if (data.type !== 'models' && data.simpleName) {
                    packageName = data.name;
                }
            }
            tnx.open(classAdd, {
                appName: appName,
                defaultValue: {packageName},
                after: (appName, model) => {
                    return this.doAddClass(appName, model);
                },
            });
        },
        doAddClass(appName, model) {
            let app = this.project.findApp(appName);
            let clazz = app.findClass(model, true);
            if (clazz) {
                tnx.error('已存在 ' + clazz.getName());
                return false;
            }
            clazz = ModelingApp.ofClass(model);
            clazz.id = tnx.util.string.uuid32();
            clazz.saveTime = new Date();
            let tempApp = app.clone();
            tempApp.addClass(clazz);
            this.save(tempApp, () => {
                app.addClass(clazz);
                tnx.toast('添加成功');
                tnx.app.eventBus.emit('app-changed', app.name);
                this.$nextTick(() => {
                    setTimeout(() => {
                        this.openClass(appName, clazz);
                    });
                });
            });
        },
        toCopyClass(data) {
            let app = this.project.findApp(data.appName);
            if (app) {
                let clazz = app.findClass(data);
                if (clazz) {
                    tnx.open(classAdd, {
                        appName: app.name,
                        defaultValue: clazz,
                        after: (appName, model) => {
                            return this.doAddClass(appName, model);
                        },
                    });
                }
            }
        },
        toShowInheritanceHierarchy(data) {
            tnx.open(inheritanceHierarchy, {
                classId: data.id,
                open: (appName, clazz) => {
                    this.toEditClassDetail(appName, clazz);
                },
            });
        },
        toModelingDependencies(appName) {
            tnx.open(modelingDependencies, {
                appName,
            });
        },
        toAppSetting(appName, tabName) {
            this.$refs.header.toAppSetting(appName, tabName);
        },
        reloadLib(data) {
            if (data.groupId === 'java') {
                tnx.confirm('jdk解析加载时间较长（需约10秒），确定开始重新加载吗？').then(yes => {
                    if (yes) {
                        data.loading = true;
                        jdkLib.load(data.version, lib => {
                            for (let i = 0; i < this.currentApp.dependencies.length; i++) {
                                let d = this.currentApp.dependencies[i];
                                if (d.groupId === lib.groupId && d.artifactId === lib.artifactId && d.version === lib.version) {
                                    this.currentApp.dependencies[i] = lib;
                                }
                            }
                            data.loading = false;
                            tnx.toast('加载完成');
                        }, true);
                    }
                });
            } else if (data.groupId === 'org.truenewx') {
                console.info(data);
            }
        },
        openClass(appName, clazz) {
            let classNodeKey = getClassNodeKey(appName, clazz);
            this.expandTreeNode(classNodeKey, true);
            this.toEditClassDetail(appName, clazz);
        },
        toUpdateClass(data) {
            let oldAppName = data.appName;
            tnx.open(classUpdate, {
                appName: oldAppName,
                classId: data.id,
                after: (newAppName, model) => {
                    let oldApp = this.project.findApp(oldAppName);
                    return this.updateClass(oldApp, data, newAppName, model, () => {
                        tnx.toast('修改成功');
                    });
                },
            });
        },
        updateClass(oldApp, oldClass, newAppName, newClass, callback) {
            let targetClass = this.project.findClass(newAppName, newClass, true);
            if (targetClass && targetClass.id !== oldClass.id) {
                tnx.error('已存在 ' + targetClass.getName());
                this.$nextTick(() => {
                    // 重新渲染导航树，以将错误移动的类恢复原样展示
                    this.resetTreeData();
                });
                return false;
            }
            let oldAppName = oldApp.name;
            if (oldAppName === newAppName) { // 所属应用未更改
                let newApp = this.project.findApp(newAppName);
                let tempApp = newApp.clone();
                newClass.saveTime = new Date();
                tempApp.updateClass(oldClass.id, newClass);
                this.save(tempApp, () => {
                    newClass = newApp.updateClass(oldClass.id, newClass);
                    PersistUtil.syncSubClasses(this.project, newClass);
                    this.$emit('class-change', oldApp, oldClass, newApp, newClass);
                    tnx.app.eventBus.emit('app-changed', newApp.name);
                    if (typeof callback === 'function') {
                        callback();
                    }
                    this.$nextTick(() => {
                        this.expandTreeNode(getPackageNodeKey(newAppName, newClass.packageName), true);
                    });
                });
            } else { // 所属应用更改
                let oldTempApp = oldApp.clone();
                let oldTempClass = oldTempApp.removeClass(oldClass);
                let newApp = this.project.findApp(newAppName);
                let newTempApp = newApp.clone();
                let newTempClass = oldTempClass.clone();
                newTempClass.update(newClass);
                newTempApp.addClass(newTempClass);
                this.save(oldTempApp, () => {
                    this.save(newTempApp, () => {
                        oldApp.removeClass(oldTempClass);
                        newApp.addClass(newTempClass);
                        this.$emit('class-change', oldApp, oldClass, newApp, newClass);
                        tnx.app.eventBus.emit('app-changed', oldApp.name);
                        tnx.app.eventBus.emit('app-changed', newApp.name);
                        if (typeof callback === 'function') {
                            callback();
                        }
                        this.$nextTick(() => {
                            this.expandTreeNode(getPackageNodeKey(newAppName, newTempClass.packageName), true);
                        });
                    });
                });
            }
        },
        toDeleteClass(data) {
            tnx.confirm('确定删除类 ' + data.name + ' 吗？').then(yes => {
                if (yes) {
                    let app = this.project.findApp(data.appName);
                    let tempApp = app.clone();
                    tempApp.removeClass(data);
                    this.save(tempApp, () => {
                        app.removeClass(data);
                        app.syncReferences(true);
                        this.$emit('class-deleted', data.id);
                        tnx.toast('删除成功');
                        tnx.app.eventBus.emit('app-changed', app.name);
                    });
                }
            });
        },
        toEditClassDetail(appName, clazz) {
            clazz = this.project.findClass(appName, clazz);
            if (clazz) {
                let app = this.project.findApp(appName);
                this.$emit('to-edit', app, clazz.id);
            }
        },
    }
}
</script>

<style lang="scss" scoped>
.el-aside {
    display: flex;
    flex-direction: column;
    border-right: 1px solid var(--el-border-color);

    :deep(.class-tree-container) {
        flex-grow: 1;
        overflow: hidden;
        padding: 4px 0;

        .el-tree {

            .el-tree-node__content {

                .el-checkbox.is-disabled {
                    display: none;
                }

                .node-name {
                    padding-bottom: 2px;
                    flex-grow: 1;
                    margin-left: 4px;
                    margin-right: 4px;
                }

                .node-action {
                    visibility: hidden;
                    flex-shrink: 0;
                    display: flex;
                    align-items: center;
                }

            }

            .el-tree-node__content:hover,
            .is-current > .el-tree-node__content {

                .node-action {
                    visibility: visible;
                }

            }
        }
    }

    :deep(.footer) {

        .el-button {
            padding: 13px;
            border-radius: 0;
            width: 100%;
        }

    }
}

.el-tabs {
    --el-tabs-header-height: 41px;
    border: none;

    .el-tabs__header {
        background-color: var(--el-color-primary);

        .el-tabs__item:not(.is-active) {
            color: var(--el-color-white);
        }
    }

    .el-tabs__content {
        padding: 0;
    }
}
</style>
